package org.hive.testserver.domain.model.schedule;

import org.hive.testserver.domain.model.Slave;
import org.hive.testserver.domain.model.Test;
import org.hive.testserver.domain.model.TestState;
import org.hive.testserver.domain.model.WorkSheet;

/**
 * If a test has already been executed but was unsuccessful, then attempt to run again up
 * until a maximum number of recovery attempts.
 */
public class RetryProblemTestAllocator extends BaseAllocatorLink {

	public static final int MAXIMUM_RECOVERY_ATTEMPTS = 1;

	private int maximumRecoveryAttempts;

	public RetryProblemTestAllocator(AllocatorLink next) {
		this(MAXIMUM_RECOVERY_ATTEMPTS, next);
	}

	public RetryProblemTestAllocator(int maximumRecoveryAttempts, AllocatorLink next) {
		super(next);
		this.maximumRecoveryAttempts = maximumRecoveryAttempts;
	}

	private static final long serialVersionUID = 1L;

	@Override
	protected ScheduleReason scheduledReason() {
		return ScheduleReason.RETRY_PROBLEM_TEST;
	}
	
	public boolean accept(Test test, Slave slave, WorkSheet workSheet) {
		if (next.accept(test, slave, workSheet)) {
			// give later allocators a chance first
			return true;
		} else if (shouldAccept(test, slave, workSheet)) {
			tests().add(test);
			return true;
		} else {
			return false;
		}
	}


	@Override
	protected boolean shouldAccept(Test test, Slave slave, WorkSheet workSheet) {
		return problemTest(test);
	}

	@Override
	public boolean shouldConsiderTestFinished(Test test) {
		return test.currentState() == TestState.PASSED || 
			((test.currentState() == TestState.ABORTED||test.currentState() == TestState.FAILED) && test.timesExecuted() > maximumRecoveryAttempts);
	}
	
	private boolean problemTest(Test test) {
		return (test.currentState() == TestState.ABORTED||test.currentState() == TestState.FAILED) && test.timesExecuted() <= maximumRecoveryAttempts;
	}

}
